/** @file    SessionShell.cpp
 *  @author  Alessandro Polo
 *  @version $Id: SessionShell.cpp 2879 2010-08-09 02:41:13Z alex $
 *  @brief
 * File containing methods for the wosh::communication::SessionShell class.
 * The header for this class can be found in SessionShell.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <framework/communication/SessionShell.h>

 #include <core/SessionProtocol.h>
 #include <framework/security/SecurityAuthenticationShell.h>
 #include <core/ObjectAllocator.h>
 #include <core/Utilities.h>
 #include <core/WoshKernel.h>
 #include <iostream>
 //#include <termios.h>

 
using namespace std;
namespace wosh {
 namespace communication {

 WOSH_REGISTER(wosh::communication::SessionShell, "wosh::Session", 1.0, _static_SessionShell )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

SessionShell::SessionShell()
	: ThreadImpl(NULL, "SessionShell"), Session() {
	Object::setName("SessionShell", getID());
	Log.setContext( Object::getName(), true );

	this->streamInput = &std::cin;
	this->streamOutput = &std::cout;

	this->restrictions = Session::ALLOW_ALL;

	this->credentials = new wosh::security::SecurityTokenProviderShell();
 }

SessionShell::~SessionShell() {
	Log(LOG_VERBOSE, ":~SessionShell() Destroying.." );
	if ( isThreadRunning() )
		close();
	delete this->credentials; this->credentials =NULL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT SessionShell::open( unsigned long timeout ) {
	Log(LOG_INFO, ":open(%ld) Starting Thread..", timeout );
	return startThread(timeout);
 }

WRESULT SessionShell::close( unsigned long timeout ) {
	Log(LOG_INFO, ":close(%ld) Stopping Thread..", timeout  );
	return quitThread(timeout);
 }

WRESULT SessionShell::printMessage( const std::string& output_text ) {
	if ( Log.isDebug() )
		Log(LOG_DEBUG, ":printMessage(%s)", output_text.c_str() );
	*this->streamOutput << output_text << std::endl;
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool SessionShell::initializingThread() {
	if ( this->streamInput == NULL || this->streamOutput == NULL ) return false;
	if ( this->protocol == NULL ) return false;
	return true;
 }

void SessionShell::runThread() {
	while( this->running ) {
		if ( handleLogin(this->running) )
			handleSession(this->running);
		if ( isFailed() ) {
			Log(LOG_CRITICAL, ":runThread() FAILURE state! Exiting loop" );
			break;
		 }
		if ( !this->streamInput->good() ) {
			Log(LOG_CRITICAL, ":runThread() Invalid input-stream! Exiting loop" );
			setState(Session::STATE_FAILURE);
			break;
		 }
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool SessionShell::handleLogin( bool& still_running ) {
	Log(LOG_VERBOSE, ":handleLogin() Handling Login.." );
	setState(Session::STATE_LOGGING);
	*this->streamOutput << std::endl;
	char buf[255+1];
	string user, pwd;
	while ( user.size() < 3 ) {
		setState(Session::STATE_LOGGING_USER);
		*this->streamOutput << "UserName: ";
		if ( !this->streamInput->good() ) {
			Log(LOG_CRITICAL, ":handleLogin() Invalid input-stream!" );
			setState(Session::STATE_FAILURE);
			return false;
		 }
		memset(buf,0,255);
		this->streamInput->getline(buf, 255);
		user.assign(buf);
		Utilities::trimSpaces(user);
		if ( !still_running ) return false;
	 }
	setState(Session::STATE_LOGGING_PWD);
	*this->streamOutput << "PassWord: ";
	if ( !this->streamInput->good() ) {
		Log(LOG_CRITICAL, ":handleLogin() Invalid input-stream!" );
		setState(Session::STATE_FAILURE);
		return false;
	 }
	this->streamInput->getline(buf, 255);
	pwd.assign(buf);
	Utilities::trimSpaces(pwd);
	if ( pwd.empty() ) {
		Log(LOG_DEBUG, ":handleLogin() Invalid empty password!" );
		return false;
	 }
	if ( !still_running ) return false;
	Log(LOG_VERBOSE, ":handleLogin() Logging In user '%s',pwd[%d]..", user.c_str(), pwd.size() );
	setUsernameLogged(user);
	WRESULT ret = login();
	if ( WFAILED(ret) ) {
		return false;
	 }
	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void SessionShell::handleSession( bool& still_running ) {
	Log(LOG_VERBOSE, ":handleSession() Handling Session.." );
	WRESULT opened = Session::open(); // base implementation!!!
	if ( WFAILED(opened) ) {
		Log(LOG_CRITICAL, ":handleSession() FAILED opening session" );
		return;
	 }
	if ( isWelcomePrompted() ) {
		bool sentok = ( *this->streamOutput << getWelcomeMessage() << std::endl );
		if ( !sentok )
			Log(LOG_WARNING, ":handleSession() FAILED sending welcome message [continuing]" );
	 }
	Log(LOG_VERBOSE, ":handleSession() Entering loop.." );
	char* buffer = new char[1024+1];
	std::string deprecated;
	while( still_running ) {
		*this->streamOutput << getPrompt();
		if ( !this->streamInput->good() ) {
			Log(LOG_CRITICAL, ":handleLogin() Invalid input-stream!" );
			setState(Session::STATE_FAILURE);
			break;
		 }
		this->streamInput->getline(buffer, 1024);
		deprecated.assign(buffer);
		parseInput( deprecated );
	 }
	delete [] buffer;
	Log(LOG_VERBOSE, ":handleSession() Exited loop.." );

	WRESULT closed = Session::close(); // base implementation!!!
	if ( WFAILED(closed) ) {
		Log(LOG_CRITICAL, ":handleSession() FAILED closing session" );
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace communication
}; // namespace wosh
